package com.lucy.flow.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Validator;
import com.lucy.common.constant.HttpStatus;
import com.lucy.common.exception.BusinessException;
import com.lucy.flow.constant.FlowNodeTypeConst;
import com.lucy.flow.domain.dto.FlowParam;
import com.lucy.flow.domain.entity.FlowDefine;
import com.lucy.flow.domain.entity.FlowNodeAssign;
import com.lucy.flow.domain.entity.FlowProcess;
import com.lucy.flow.domain.entity.FlowWork;
import com.lucy.flow.event.FlowWorkCreatedEvent;
import com.lucy.flow.loader.TaskUserLoader;
import com.lucy.flow.model.BaseModel;
import com.lucy.flow.model.FlowModel;
import com.lucy.flow.model.StartModel;
import com.lucy.flow.model.TaskModel;
import com.lucy.flow.service.FlowDefineService;
import com.lucy.flow.service.FlowWorkService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.domain.Example;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.List;
import java.util.Optional;

/**
 * @author kindear
 * 流程实例-聚合管理
 */
@Slf4j
@Service
public class FlowWorkCtl {
    /**
     * 应用上下文注册
     */
    @Resource
    ApplicationContext context;

    @Autowired
    FlowDefineService flowDefineService;

    @Autowired
    FlowWorkService flowWorkService;

    @Autowired
    FlowNodeAssignService assignService;

    @Autowired
    FlowProcessService processService;

    @Autowired
    TaskUserLoader userLoader;

    /**
     * 启动流程
     * @param flowDefineCode 流程定义编码
     * @param operator 操作人ID
     * @param flowParam 流程参数 FlowParam
     * @return FlowWork 流程实例信息
     */
    public FlowWork startProcess(String flowDefineCode, String operator, FlowParam flowParam){
        // 1. 获取流程定义信息
        Optional<FlowDefine> defineOp = flowDefineService.getByDefineCode(flowDefineCode);
        if (!defineOp.isPresent()){
            log.debug("{}对应流程定义不存在",flowDefineCode);
            throw new BusinessException(HttpStatus.NOT_FOUND,"流程定义不存在");
        }
        FlowDefine define = defineOp.get();
        // 2. 获取当前操作人ID
        if (Validator.isEmpty(operator)){
            throw new BusinessException(HttpStatus.UNAUTHORIZED,"未登录");
        }
        // 3. 获取流程模型
        FlowModel flowModel = define.getFlowModel();
        // 4. 获取开始节点
        StartModel startModel = flowModel.getStartModel();
        // 5. 构建流程参数
        FlowWork flowWork = new FlowWork(define,operator,flowModel);
        // 6. 构建业务相关内容
        flowWork.setFlowParam(flowParam);
        flowWork.setBizId(flowParam.getBizId());
        flowWork.setBizType(flowParam.getBizType());
        // 7. 写入数据库
        FlowWork execRes = flowWorkService.saveOne(flowWork);
        // 8. 发布事件
        context.publishEvent(new FlowWorkCreatedEvent(this,execRes));
        // 9. 返回流程实例
        return execRes;
    }

    /**
     * 流程审核任务完成
     * @param flowWorkId 流程实例ID
     * @param operator 操作人ID
     * @param remark 批复意见
     * @return 流程实例信息
     */
    @Transactional
    public FlowWork completeTask(String flowWorkId, String operator, String remark){
        // 1. 获取流程实例信息
        FlowWork flowWork = flowWorkService.getOneUnwrap(flowWorkId);
        // 2. 获取流程实例 模型
        FlowModel flowModel = flowWork.getFlowModel();
        // 3. 获取流程实例当前节点
        String currentNodeId = flowWork.getCurrentNodeId();
        BaseModel currentNodeModel = flowModel.getNodeModel(currentNodeId);
        if (!currentNodeModel.getNodeType().equals(FlowNodeTypeConst.TASK_NODE)){
            throw new BusinessException(HttpStatus.FORBIDDEN,"流程实例不在审核态");
        }
        // 4. 获取TASK 节点信息
        TaskModel taskModel = (TaskModel) currentNodeModel;
        // 5. 查询当前节点分配信息
        FlowNodeAssign assign = new FlowNodeAssign();
        assign.setFlowDefineCode(flowWork.getFlowDefineCode());
        assign.setFlowNodeId(flowWork.getCurrentNodeId());
        Example<FlowNodeAssign> example = Example.of(assign);
        List<FlowNodeAssign> assigns = assignService.getExample(example);
        // 6. 获取当前节点审核人员
        List<Long> userIds = userLoader.load(assigns);
        if (!userIds.contains(Long.parseLong(operator))){
            throw new BusinessException(HttpStatus.FORBIDDEN,"该用户未授权该操作");
        }
        // 7. 新增审核记录
        FlowProcess flowProcess = new FlowProcess();
        // @TODO 使用构建器
        return flowWork;
    }
}
